/**
* <p>Title: HoleGUI</p>
* <p>Description: CS343 Project</p>
* @author Derek Meisner
* @author Conor Lauer
* @author Derek Thompson
* @email meis0056@umn.edu
* @email conor.lauer@my.uwrf.edu
* @email derek.thompson-1@my.uwrf.edu
* @date 11/14/2012
* @team Derek, Conor, Derek
*/

package golfScore;

import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.datatransfer.*;

import javax.swing.BorderFactory;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.DropMode;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JList;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.TransferHandler;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;

public class HoleGUI extends JFrame implements ActionListener{

	/**
	 * Data Members
	 */
	private static final long serialVersionUID = 1L;
	private final int FRAME_X = 300;
	private final int FRAME_Y = 200;
	private final int FRAME_WIDTH = 350;
	private final int FRAME_HEIGHT = 450;
	
	private JTable table;
	private DefaultTableModel tablemodel;
	private final int TABLE_X = 0, TABLE_Y = 35, TABLE_WIDTH = 335, TABLE_HEIGHT = 30;
	private JButton save;
	private final int SAVE_X = 240, SAVE_Y = 0, SAVE_WIDTH = 40, SAVE_HEIGHT = 27;
	private JButton done;
	private final int DONE_X = 280, DONE_Y = 0, DONE_WIDTH = 40, DONE_HEIGHT = 27;
	private JLabel name;
	private final int NAME_X = 0, NAME_Y = 0, NAME_WIDTH = 800, NAME_HEIGHT = 30;
	private JList list1;
	private final int LIST1_X = 20, LIST1_Y = 320, LIST1_WIDTH = 145, LIST1_HEIGHT = 48;
	private JList list2;
	private final int LIST2_X = 165, LIST2_Y = 320, LIST2_WIDTH = 145, LIST2_HEIGHT = 48;
	private JButton spinUp;
	private final int SPIN_UP_X = 255, SPIN_UP_Y = 250, SPIN_UP_WIDTH = 40, SPIN_UP_HEIGHT = 27;
	private JButton spinDown;
	private final int SPIN_DOWN_X = 255, SPIN_DOWN_Y = 278, SPIN_DOWN_WIDTH = 40, SPIN_DOWN_HEIGHT = 27;
	private JButton next;
	private final int NEXT_X = 195, NEXT_Y = 370, NEXT_WIDTH = 75, NEXT_HEIGHT = 27;
	private JButton back;
	private final int BACK_X = 65, BACK_Y = 370, BACK_WIDTH = 75, BACK_HEIGHT = 27;
	private JLabel scoreHeader;
	private final int SCORE_HEADER_X = 165, SCORE_HEADER_Y = 195, SCORE_HEADER_WIDTH = 145, SCORE_HEADER_HEIGHT = 48;
	private JLabel score;
	private final int SCORE_X = 180, SCORE_Y = 250, SCORE_WIDTH = 75, SCORE_HEIGHT = 54;
	private JLabel roundHeader;
	private final int ROUND_HEADER_X = 0, ROUND_HEADER_Y = 115, ROUND_HEADER_WIDTH = 335, ROUND_HEADER_HEIGHT = 48;
	private JLabel round;
	private final int ROUND_X = 115, ROUND_Y = 170, ROUND_WIDTH = 105, ROUND_HEIGHT = 54;
	private JLabel userAverageHeader;
	private final int USER_AVERAGE_HEADER_X = 0, USER_AVERAGE_HEADER_Y = 220, USER_AVERAGE_HEADER_WIDTH = 335, USER_AVERAGE_HEADER_HEIGHT = 48;
	private JLabel finish;
	private final int FINISH_X = 0, FINISH_Y = 70, FINISH_WIDTH = 335, FINISH_HEIGHT = 64;
	private JLabel holeNumHeader;
	private final int HOLENUM_HEADER_X = 0, HOLENUM_HEADER_Y = 70, HOLENUM_HEADER_WIDTH = 335, HOLENUM_HEADER_HEIGHT = 64;
	private JLabel holeInfo1;
	private final int HOLE_INFO1_X = 0, HOLE_INFO1_Y = 129, HOLE_INFO1_WIDTH = 335, HOLE_INFO1_HEIGHT = 24;
	private JLabel holeInfo2;
	private final int HOLE_INFO2_X = 0, HOLE_INFO2_Y = 153, HOLE_INFO2_WIDTH = 335, HOLE_INFO2_HEIGHT = 24;
	private JLabel parHeader;
	private final int PAR_HEADER_X = 20, PAR_HEADER_Y = 195, PAR_HEADER_WIDTH = 145, PAR_HEADER_HEIGHT = 48;
	private JLabel par;
	private final int PAR_X = 20, PAR_Y = 255, PAR_WIDTH = 145, PAR_HEIGHT = 48;

	private Hole currentHole;
	private Course course;
	private User user;
	private XML_Interactions doc;
	private int[] userScores;

	/**
	   * Constructor
	   * @require
	   * @ensure
	   */
	public HoleGUI(){
		super("");
		this.setUpGUI();
	}
	
	/**
	   * Constructor to give title, course, user, and document instance
	   * @require
	   * @ensure 
	   */
	public HoleGUI(String title, Course course, User user, XML_Interactions instanceDoc){
		super(title);
		this.course = course;
		currentHole = course.getHoles().get(0);
		this.user = user;
		this.setUpGUI();
		this.doc = instanceDoc;
	}
	
	/**
	   * Setup GUI
	   * @require
	   * @ensure 
	   */
	private void setUpGUI(){
		setBounds(FRAME_X, FRAME_Y, FRAME_WIDTH, FRAME_HEIGHT);
		addWindowListener( new WindowExitAdapter() );
		
		//This allows me to format the JFrame. If not set to null, formatting is done automatically.
		setLayout(null);
		
		userScores = new int[course.getHoles().size()];
		
		//Create table model with number of Rows and number of Columns
		tablemodel = new DefaultTableModel(2, course.getHoles().size());
		
		//Populate table
		for(int x = 0; x < tablemodel.getRowCount(); x++)
		{
			for( int y = 0; y < tablemodel.getColumnCount(); y++)
			{
				if(x == 0)
					tablemodel.setValueAt(y + 1, x, y);
				else
					tablemodel.setValueAt(0, x, y);
			}
		}
		table = new JTable(tablemodel);
		
		//Center text in table
		DefaultTableCellRenderer dtcr = new DefaultTableCellRenderer();
		dtcr.setHorizontalAlignment(SwingConstants.CENTER);
		
		table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
		for (int i = 0; i < tablemodel.getColumnCount(); i++)
		{
			table.getColumnModel().getColumn(i).setWidth(TABLE_WIDTH / course.getHoles().size());
			table.getColumnModel().getColumn(i).setCellRenderer(dtcr);
		}
		
		table.setBounds(TABLE_X, TABLE_Y, TABLE_WIDTH, TABLE_HEIGHT);
		table.setEnabled(false);
		add( table );

		//Setting up user name header
		name = new JLabel("Name: " + user.getName() +"");
		add (name);
		name.setBounds(NAME_X, NAME_Y, NAME_WIDTH, NAME_HEIGHT);
		Font curFont = name.getFont();
		name.setFont(new Font(curFont.getFontName(), Font.BOLD, 12));
		
		//Setting up save button
		save = new JButton("save");
		save.addActionListener(this);
		save.setBorder(BorderFactory.createRaisedBevelBorder());
		save.setBounds(SAVE_X, SAVE_Y, SAVE_WIDTH, SAVE_HEIGHT);
		add (save);
		
		//Setting up save button
		done = new JButton("done");
		done.addActionListener(this);
		done.setBorder(BorderFactory.createRaisedBevelBorder());
		done.setBounds(DONE_X, DONE_Y, DONE_WIDTH, DONE_HEIGHT);
		add (done);
		done.setVisible(false);
		
		//Setting up hole header
		holeNumHeader = new JLabel(currentHole.getHoleHead(), JLabel.CENTER);
		add (holeNumHeader);
		holeNumHeader.setBounds(HOLENUM_HEADER_X, HOLENUM_HEADER_Y, HOLENUM_HEADER_WIDTH, HOLENUM_HEADER_HEIGHT);
		curFont = holeNumHeader.getFont();
		holeNumHeader.setFont(new Font(curFont.getFontName(), Font.BOLD, 64));
		
		//Setting up score
		finish = new JLabel("Round Over - Save Scores!", JLabel.CENTER);
		add (finish);
		finish.setBounds (FINISH_X, FINISH_Y, FINISH_WIDTH, FINISH_HEIGHT);
		curFont = finish.getFont();
		finish.setFont(new Font(curFont.getFontName(), Font.BOLD, 24));
		finish.setVisible(false);
		
		//Setting up score
		userAverageHeader = new JLabel("0", JLabel.CENTER);
		add (userAverageHeader);
		userAverageHeader.setBounds (USER_AVERAGE_HEADER_X, USER_AVERAGE_HEADER_Y, USER_AVERAGE_HEADER_WIDTH, USER_AVERAGE_HEADER_HEIGHT);
		curFont = userAverageHeader.getFont();
		userAverageHeader.setFont(new Font(curFont.getFontName(), Font.BOLD, 18));
		userAverageHeader.setVisible(false);
		
		//Setting up hole info
		holeInfo1 = new JLabel("Distance: " + currentHole.getTotalDistance(), JLabel.CENTER);
		add (holeInfo1);
		holeInfo1.setBounds(HOLE_INFO1_X, HOLE_INFO1_Y, HOLE_INFO1_WIDTH, HOLE_INFO1_HEIGHT);
		curFont = holeInfo1.getFont();
		holeInfo1.setFont(new Font(curFont.getFontName(), Font.BOLD, 20));
		
		//Setting up hole info
		holeInfo2 = new JLabel("Handicap: " + currentHole.getHandicap(), JLabel.CENTER);
		add (holeInfo2);
		holeInfo2.setBounds(HOLE_INFO2_X, HOLE_INFO2_Y, HOLE_INFO2_WIDTH, HOLE_INFO2_HEIGHT);
		curFont = holeInfo2.getFont();
		holeInfo2.setFont(new Font(curFont.getFontName(), Font.BOLD, 20));

		//Setting up par header
		parHeader = new JLabel("Par", JLabel.CENTER);
		add (parHeader);
		parHeader.setBounds(PAR_HEADER_X, PAR_HEADER_Y, PAR_HEADER_WIDTH, PAR_HEADER_HEIGHT);
		curFont = parHeader.getFont();
		parHeader.setFont(new Font(curFont.getFontName(), Font.BOLD, 48));
		
		//Setting up par for hole
		par = new JLabel(String.format("%d", currentHole.getPar()), JLabel.CENTER);
		add (par);
		par.setBounds(PAR_X, PAR_Y, PAR_WIDTH, PAR_HEIGHT);
		curFont = par.getFont();
		par.setFont(new Font(curFont.getFontName(), Font.BOLD, 48));
		
		//Setting up score header
		scoreHeader = new JLabel("Score", JLabel.CENTER);
		add (scoreHeader);
		scoreHeader.setBounds(SCORE_HEADER_X, SCORE_HEADER_Y, SCORE_HEADER_WIDTH, SCORE_HEADER_HEIGHT);
		curFont = scoreHeader.getFont();
		scoreHeader.setFont(new Font(curFont.getFontName(), Font.BOLD, 48));
		
		//Setting up score
		score = new JLabel(String.format("%d", currentHole.getPar()), JLabel.CENTER);
		score.setBorder(BorderFactory.createLoweredBevelBorder());
		add (score);
		score.setBounds (SCORE_X, SCORE_Y, SCORE_WIDTH, SCORE_HEIGHT);
		curFont = score.getFont();
		score.setFont(new Font(curFont.getFontName(), Font.BOLD, 48));
		
		//Setting up total score header
		roundHeader = new JLabel("Score", JLabel.CENTER);
		add (roundHeader);
		roundHeader.setBounds(ROUND_HEADER_X, ROUND_HEADER_Y, ROUND_HEADER_WIDTH, ROUND_HEADER_HEIGHT);
		curFont = roundHeader.getFont();
		roundHeader.setFont(new Font(curFont.getFontName(), Font.BOLD, 36));
		roundHeader.setVisible(false);
		
		//Setting up total score
		round = new JLabel("", JLabel.CENTER);
		round.setBorder(BorderFactory.createLoweredBevelBorder());
		add (round);
		round.setBounds (ROUND_X, ROUND_Y, ROUND_WIDTH, ROUND_HEIGHT);
		curFont = round.getFont();
		round.setFont(new Font(curFont.getFontName(), Font.BOLD, 48));
		round.setVisible(false);
		
		//Setting up score spinup
		spinUp = new JButton("up");
		spinUp.addActionListener(this);
		spinUp.setBorder(BorderFactory.createRaisedBevelBorder());
		spinUp.setBounds(SPIN_UP_X, SPIN_UP_Y, SPIN_UP_WIDTH, SPIN_UP_HEIGHT);
		add (spinUp);
		
		//Setting up score spindown
		spinDown = new JButton("down");
		spinDown.addActionListener(this);
		spinDown.setBorder(BorderFactory.createRaisedBevelBorder());
		spinDown.setBounds(SPIN_DOWN_X, SPIN_DOWN_Y, SPIN_DOWN_WIDTH, SPIN_DOWN_HEIGHT);
		add (spinDown);
		
		//Setting up next hole
		next = new JButton("next");
		next.addActionListener(this);
		next.setBorder(BorderFactory.createRaisedBevelBorder());
		next.setBounds(NEXT_X, NEXT_Y, NEXT_WIDTH, NEXT_HEIGHT);
		add (next);
		
		//Setting up previous hole
		back = new JButton("back");
		back.addActionListener(this);
		back.setBorder(BorderFactory.createRaisedBevelBorder());
		back.setBounds(BACK_X, BACK_Y, BACK_WIDTH, BACK_HEIGHT);
		add (back);
		
		//creating list1 - probably a more elegant way to do this, but it works
		DefaultListModel listModel = new DefaultListModel();
		listModel.addElement("back-->");
		list1 = new JList(listModel);
		list1.setBounds(LIST1_X, LIST1_Y, LIST1_WIDTH, LIST1_HEIGHT);
		list1.setBorder(BorderFactory.createLoweredBevelBorder());
		curFont = list1.getFont();
		list1.setFont(new Font(curFont.getFontName(), Font.BOLD, 30));
		DefaultListCellRenderer renderer = (DefaultListCellRenderer)list1.getCellRenderer();
		renderer.setVerticalAlignment(JLabel.CENTER);
		
		list1.setTransferHandler(new TransferHandler(){
			private static final long serialVersionUID = 1L;
			
			public boolean canImport(TransferHandler.TransferSupport info) {
				// we only import Strings
				if (!info.isDataFlavorSupported(DataFlavor.stringFlavor)) {
					return false;
				}
				return true;
			}
			
			public boolean importData(TransferHandler.TransferSupport info) {
				if (!info.isDrop()) 
					return false;
				goToNext();
				return false;
			}
			
			public int getSourceActions(JComponent c) {
				return COPY;
			}
			
			protected Transferable createTransferable(JComponent c) {
				return new StringSelection("");
			}
		});
		
		list1.setDragEnabled(true);
		list1.setDropMode(DropMode.ON);
		add (list1);
		
		//creating list2 - probably a more elegant way to do this, but it works
		listModel = new DefaultListModel();
		listModel.addElement("<--next");
		list2 = new JList(listModel);
		list2.setBounds(LIST2_X, LIST2_Y, LIST2_WIDTH, LIST2_HEIGHT);
		list2.setBorder(BorderFactory.createLoweredBevelBorder());
		curFont = list2.getFont();
		list2.setFont(new Font(curFont.getFontName(), Font.BOLD, 30));
		renderer = (DefaultListCellRenderer)list2.getCellRenderer();
		renderer.setHorizontalAlignment(JLabel.RIGHT);
		renderer.setVerticalAlignment(JLabel.CENTER);
		
		list2.setTransferHandler(new TransferHandler(){
			private static final long serialVersionUID = 1L;

			public boolean canImport(TransferHandler.TransferSupport info) {
				// we only import Strings
				if (!info.isDataFlavorSupported(DataFlavor.stringFlavor)) {
					return false;
				}
				return true;
			}
			
			public boolean importData(TransferHandler.TransferSupport info) {
				if (!info.isDrop()) 
					return false;
				goToPrev();
				return false;
			}
			
			public int getSourceActions(JComponent c) {
				return COPY;
			}
			
			protected Transferable createTransferable(JComponent c) {
				return new StringSelection("");
			}
		});
		
		list2.setDragEnabled(true);
		list2.setDropMode(DropMode.ON);
		add (list2);
		
		setVisible(true);
	}
	
	/**
	   * action performed
	   * @require actionEvent
	   * @ensure
	   */
	public void actionPerformed(ActionEvent arg0) {
		if (arg0.getSource().equals( spinUp )){
			score.setText(String.format("%d", Integer.parseInt(score.getText())+1));	
		
		}else if (arg0.getSource().equals( spinDown )){
			if (Integer.parseInt(score.getText()) > 1)
				score.setText(String.format("%d", Integer.parseInt(score.getText())-1));
		
		}else if (arg0.getSource().equals( next )){
			this.goToNext();
		}else if (arg0.getSource().equals( back )){
			this.goToPrev();
		}else if(arg0.getSource().equals ( save )){
			this.saveScore();
		}else if(arg0.getSource().equals( done )){
			this.done();
		}
	}

	/**
	   * Set currentHole
	   * @require
	   * @ensure this.currentHole = currentHole
	   */
	public void setCurrentHole(Hole currentHole) {
		assert (this.currentHole == currentHole);
		this.currentHole = currentHole;
	}

	/**
	   * Gets current hole
	   * @require
	   * @ensure 
	   */
	public Hole getCurrentHole() {
		return currentHole;
	}
	
	/**
	   * sets current hole to next hole
	   * @require
	   * @ensure future currentHole = old currentHol +1
	   */
	public void nextHole(){
		int currentHoleVal = currentHole.getHoleVal();
		this.setCurrentHole(course.holes.get(currentHoleVal));
	}
		
	/**
	   * sets current hole to previous hole
	   * @require
	   * @ensure future currentHole = old currentHol -1
	   */
	public void previousHole(){
		int currentHoleVal = currentHole.getHoleVal();
		this.setCurrentHole(course.holes.get(currentHoleVal-2));
	}
	
	/**
	   * getRoundScore
	   * @require
	   * @ensure roundScore >= 0
	   */
	public int getRoundScore(){
		int roundScore = 0;
		for (int i : userScores)
			roundScore = roundScore + i;
		
		return roundScore;
	}
	
	/**
	   * goToNext
	   * @require
	   * @ensure 
	   */
	public void goToNext(){
		if (currentHole.getHoleVal() > 0){
			if (currentHole.getHoleVal() == course.holes.size() && !finish.isVisible()){
				userScores[currentHole.getHoleVal()-1] = Integer.parseInt(score.getText());
				
				XML_Interactions lastPage = new XML_Interactions(doc.getUserFilePath(), course, user, userScores, false);
				lastPage.saveLogic();
				lastPage.setAverageLogic();
				
				tablemodel.setValueAt(Integer.parseInt(score.getText()), 1, currentHole.getHoleVal()-1);
				round.setText(String.format("%d", this.getRoundScore()));
				userAverageHeader.setText(String.format("%s Average = %.2f", user.getName(), user.getAverage()));
				holeNumHeader.setVisible(false);
				holeInfo1.setVisible(false);
				holeInfo2.setVisible(false);
				spinUp.setVisible(false);
				spinDown.setVisible(false);
				parHeader.setVisible(false);
				par.setVisible(false);
				scoreHeader.setVisible(false);
				score.setVisible(false);
				finish.setVisible(true);
				roundHeader.setVisible(true);
				round.setVisible(true);
				userAverageHeader.setVisible(true);
				done.setVisible(true);
			}else if (!finish.isVisible()){
				userScores[currentHole.getHoleVal()-1] = Integer.parseInt(score.getText());
				tablemodel.setValueAt(Integer.parseInt(score.getText()), 1, currentHole.getHoleVal()-1);
				this.nextHole();
				holeNumHeader.setText(currentHole.getHoleHead());
				holeInfo1.setText("Distance: " + currentHole.getTotalDistance());
				holeInfo2.setText("Handicap: " + currentHole.getHandicap());
				par.setText(String.format("%d", currentHole.getPar()));
				if (userScores[currentHole.getHoleVal()-1] != 0)
					score.setText(String.format("%d", userScores[currentHole.getHoleVal()-1]));
				else
					score.setText(String.format("%d", currentHole.getPar()));
			}
		}
	}
	
	/**
	   * goToPrev
	   * @require
	   * @ensure
	   */
	public void goToPrev(){
		if (finish.isVisible()){
			finish.setVisible(false);
			roundHeader.setVisible(false);
			round.setVisible(false);
			userAverageHeader.setVisible(false);
			done.setVisible(false);
			holeNumHeader.setVisible(true);
			holeInfo1.setVisible(true);
			holeInfo2.setVisible(true);
			spinUp.setVisible(true);
			spinDown.setVisible(true);
			parHeader.setVisible(true);
			par.setVisible(true);
			scoreHeader.setVisible(true);
			score.setVisible(true);
		}else if (currentHole.getHoleVal() > 1){
			userScores[currentHole.getHoleVal()-1] = Integer.parseInt(score.getText());
			tablemodel.setValueAt(Integer.parseInt(score.getText()), 1, currentHole.getHoleVal()-1);
			this.previousHole();
			holeNumHeader.setText(currentHole.getHoleHead());
			holeInfo1.setText("Distance: " + currentHole.getTotalDistance());
			holeInfo2.setText("Handicap: " + currentHole.getHandicap());
			par.setText(String.format("%d", currentHole.getPar()));
			if (userScores[currentHole.getHoleVal()-1] != 0)
				score.setText(String.format("%d", userScores[currentHole.getHoleVal()-1]));
			else
				score.setText(String.format("%d", currentHole.getPar()));
		}
	}
	
	/**
	   * saveScore
	   * @require
	   * @ensure
	   */
	public void saveScore(){
		
		Thread saveThread = new XML_Interactions(doc.getUserFilePath(), course, user, userScores, false);
		saveThread.start();
		
	}
	
	/**
	   * saveScore
	   * @require
	   * @ensure
	   */
	public void done(){
		//program is done.  need to increment user instance and quit
		Thread doneThread = new XML_Interactions(doc.getUserFilePath(), course, user, userScores, true);
		doneThread.start();
		
		this.dispose();

	}
	
	private class WindowExitAdapter extends WindowAdapter{

		public void windowClosing( WindowEvent e){
			//program is done.  need to increment user instance and quit
			Thread doneThread = new XML_Interactions(doc.getUserFilePath(), course, user, userScores, true);
			doneThread.start();
		}
	}	
}